package router

import (
	"encoding/json"
	"fmt"
	"net/http"
	"project/schedule/model"
	"project/schedule/parser"
	"project/schedule/safe"
	"sort"
)

// 项目排班接口
func Schedule(writer http.ResponseWriter, request *http.Request) {
	response := model.Response{}
	var body map[string]interface{}
	err := safe.BodyCheck(request, &body)
	if err != nil {
		response = model.Response{Code: 500, Message: fmt.Sprintf("%s", err)}
		_ = json.NewEncoder(writer).Encode(response)
		return
	}

	general := &model.General{}
	//fmt.Printf("%p, %v\n", general, *general)
	//fmt.Printf("%p, %v\n", &general.Descend, general.Descend)
	//fmt.Printf("%p, %v, %t\n", &general.ProjectScale, general.ProjectScale, general.ProjectScale == nil)
	//fmt.Printf("%p, %v, %t\n", &general.AuditRule, general.AuditRule, general.AuditRule == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Staff, general.Staff, general.Staff == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Project, general.Project, general.Project == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Department, general.Department, general.Department == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Allocation, general.Allocation, general.Allocation == nil)
	//fmt.Printf("%p, %v, %t\n", &general.ProjectQueue, general.ProjectQueue, general.ProjectQueue == nil)
	/*
		结构体中字段的地址是连续的, 创建结构图变量时, 也会创建所有字段对应的默认零值
		0xc000058480, {false map[] map[] map[] map[] map[] map[] <nil>}
		0xc000058480, false
		0xc000058488, map[], true
		0xc000058490, map[], true
		0xc000058498, map[], true
		0xc0000584a0, map[], true
		0xc0000584a8, map[], true
		0xc0000584b0, map[], true
		0xc0000584b8, <nil>, true
	*/

	general.ProjectScale = make(map[string]string)
	general.AuditRule = make(map[string]map[string]map[string]int)
	general.Staff = make(map[string]*model.Staff)
	general.Project = make(map[string]*model.Project)
	general.Department = make(map[string]*model.Department)
	general.Allocation = make(map[string]*model.Allocation)
	general.ProjectQueue = &model.FIFOQueue{}
	//fmt.Printf("%p, %v\n", general, *general)
	//fmt.Printf("%p, %v\n", &general.Descend, general.Descend)
	//fmt.Printf("%p, %v, %t\n", &general.ProjectScale, general.ProjectScale, general.ProjectScale == nil)
	//fmt.Printf("%p, %v, %t\n", &general.AuditRule, general.AuditRule, general.AuditRule == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Staff, general.Staff, general.Staff == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Project, general.Project, general.Project == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Department, general.Department, general.Department == nil)
	//fmt.Printf("%p, %v, %t\n", &general.Allocation, general.Allocation, general.Allocation == nil)
	//fmt.Printf("%p, %v, %t\n", &general.ProjectQueue, general.ProjectQueue, general.ProjectQueue == nil)
	/*
		0xc000058480, {false map[] map[] map[] map[] map[] map[] 0xc00017e0c0}
		0xc000058480, false
		0xc000058488, map[], false
		0xc000058490, map[], false
		0xc000058498, map[], false
		0xc0000584a0, map[], false
		0xc0000584a8, map[], false
		0xc0000584b0, map[], false
		0xc0000584b8, &{0 0 []}, false
	*/

	err = parser.ParseDescend(general, &body)
	if err != nil {
		response = model.Response{Code: 500, Message: fmt.Sprintf("%s", err)}
		_ = json.NewEncoder(writer).Encode(response)
		return
	}

	err = parser.ParseAuditRule(general, &body)
	if err != nil {
		response = model.Response{Code: 500, Message: fmt.Sprintf("%s", err)}
		_ = json.NewEncoder(writer).Encode(response)
		return
	}

	err = parser.ParseProjectScale(general, &body)
	if err != nil {
		response = model.Response{Code: 500, Message: fmt.Sprintf("%s", err)}
		_ = json.NewEncoder(writer).Encode(response)
		return
	}

	err = parser.ParseData(general, &body)
	if err != nil {
		response = model.Response{Code: 500, Message: fmt.Sprintf("%s", err)}
		_ = json.NewEncoder(writer).Encode(response)
		return
	}

	general.ProjectQueue = &model.FIFOQueue{}

	// 先排事先指定日期的项目
	retry := make(map[string]int)
	maxRetry := 500
	var proposedProjectSlice model.ProjectSlice
	for _, project := range general.Project {
		if project.ProposedStartDate != "" {
			proposedProjectSlice = append(proposedProjectSlice, project)
			retry[project.Name] = 0
		}
	}
	if proposedProjectSlice != nil {
		sort.Sort(proposedProjectSlice)
		if general.Descend {
			proposedProjectSlice.Reverse()
		}
		for _, project := range proposedProjectSlice {
			_ = general.ProjectQueue.Put(project)
		}
		for {
			if general.ProjectQueue.Empty() {
				break
			}
			project, _ := general.ProjectQueue.Get()
			err = model.AllocateProposed(general, project)
			if err != nil {
				retry[project.Name] += 1
				_ = general.ProjectQueue.Put(project)
			}
			for p, count := range retry {
				if count > maxRetry {
					response = model.Response{Code: 500, Message: fmt.Sprintf("%s尝试分配%d次失败", p, maxRetry)}
					_ = json.NewEncoder(writer).Encode(response)
					return
				}
			}
		}
	}

	// 后排事先未指定日期的项目
	retry = make(map[string]int)
	var unProposedProjectSlice model.ProjectSlice
	for _, project := range general.Project {
		if project.ProposedStartDate == "" {
			unProposedProjectSlice = append(unProposedProjectSlice, project)
		}
	}
	if unProposedProjectSlice != nil {
		sort.Sort(unProposedProjectSlice)
		if general.Descend {
			unProposedProjectSlice.Reverse()
		}
		for _, project := range unProposedProjectSlice {
			_ = general.ProjectQueue.Put(project)
		}
		for {
			if general.ProjectQueue.Empty() {
				break
			}
			project, _ := general.ProjectQueue.Get()
			err = model.AllocateUnProposed(general, project)
			if err != nil {
				retry[project.Name] += 1
				_ = general.ProjectQueue.Put(project)
			}
			for p, count := range retry {
				if count > maxRetry {
					response = model.Response{Code: 500, Message: fmt.Sprintf("%s尝试分配%d次失败", p, maxRetry)}
					_ = json.NewEncoder(writer).Encode(response)
					return
				}
			}
		}
	}

	//for project, allocate := range general.Allocation {
	//	fmt.Println(project, allocate.GroupJudge, allocate.GroupLeader, allocate.GroupMember,
	//	allocate.DepartmentProposedStartDate, allocate.ProjectProposedStartDate, allocate.ProjectAllocatedStartDate,
	//	allocate.ProjectAllocatedEndDate, allocate.ProjectProposedDay, allocate.ProjectProposedPerson)
	//	for k, v := range allocate.ProjectAllocatedDetail {
	//		fmt.Println(k, v)
	//	}
	//	fmt.Println()
	//}
	//
	//for k, v := range general.Staff {
	//	fmt.Println(k, v.WorkRegion, v.Vacation)
	//}

	response = model.Response{Code: 200, Message: "success"}
	_ = json.NewEncoder(writer).Encode(response)
}
